home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 1 / Gold Medal Software Volume 1 (Gold Medal) (1994).iso / prog / tpwprog3.arj / UGRAPHIC.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-07-02  |  26.9 KB  |  1,031 lines

  1. {******************************************************************}
  2. {                                                                  }
  3. {     Mancala                                                      }
  4. {     Turbo Pascal for Windows                                     }
  5. {     Copyright (c) 1991 by Swan Software. All rights reserved.    }
  6. {                                                                  }
  7. {******************************************************************}
  8.  
  9. { ugraphic.pas -- Graphics support module for Mancala }
  10.  
  11. unit UGraphics;
  12.  
  13. interface
  14.  
  15. uses WinTypes, WinProcs, Strings, UGlobals, UEval, UMove, Idents;
  16.  
  17.  
  18. procedure DisplayMessage(DC: HDC; StrIndex: Integer);
  19. procedure PutPebble(DC: HDC; P: TPoint);
  20. procedure MovePebble(DC: HDC; Fp, Tp: TPoint);
  21. procedure EmptyTheCup(DC: HDC; CupNum: CupIndex);
  22. procedure DisplayNumber(DC: HDC; CupNum: CupIndex; N: Integer);
  23. procedure DrawPebbles(DC: HDC; CupNum: CupIndex; NumPebbles: Integer);
  24. procedure PickUpPebbles(DC: HDC; CupNum: CupIndex; Side: Integer;
  25.   var Gameboard: Board);
  26. procedure DrawGameboard(DC: HDC; var Gameboard: Board);
  27. procedure MakeGraphMove(DC: HDC; Position: BoardRec; Side: Integer;
  28.   Move: OneMove);
  29. procedure InitUGraphics;
  30.  
  31.  
  32. implementation
  33.  
  34. const
  35.  
  36. {- FlashBuf stores the bits for an image used to flash cups on and off
  37.    during the computer's moves and for captures as a device to alert your
  38.    attention to where the action will occur next. }
  39.  
  40.   FlashBuf: array[0 .. 113] of Byte = (
  41.     $80, $00, $00, $00, $00, $01,
  42.     $E0, $00, $00, $00, $00, $07,
  43.     $F0, $00, $00, $00, $00, $0F,
  44.     $7C, $00, $00, $00, $00, $3E,
  45.     $7F, $80, $00, $00, $01, $FE,
  46.     $3F, $F0, $00, $00, $0F, $FC,
  47.     $3F, $FF, $80, $01, $FF, $FC,
  48.     $1F, $FF, $FF, $FF, $FF, $F8,
  49.     $0F, $FF, $FF, $FF, $FF, $F0,
  50.     $0F, $FF, $FF, $FF, $FF, $F0,
  51.     $07, $FF, $FF, $FF, $FF, $E0,
  52.     $03, $FF, $FF, $FF, $FF, $C0,
  53.     $01, $FF, $FF, $FF, $FF, $80,
  54.     $00, $FF, $FF, $FF, $FF, $00,
  55.     $00, $3F, $FF, $FF, $FC, $00,
  56.     $00, $1F, $FF, $FF, $F8, $00,
  57.     $00, $07, $FF, $FF, $E0, $00,
  58.     $00, $01, $FF, $FF, $80, $00,
  59.     $00, $00, $1F, $F8, $00, $00
  60.   );
  61.  
  62.  
  63. var
  64.  
  65.  
  66. {- The MessageRect rectangle outlines the area where various messages
  67. appear during the game. }
  68.  
  69.   MessageRect: TRect;
  70.  
  71.  
  72. {- The Seed value starts a new random number sequence for the Rand
  73. function, which is used to place pebbles at random, but repeatable, postions
  74. inside cups. }
  75.  
  76.   Seed: Integer;
  77.  
  78.  
  79. {- Delay for MSecs milliseconds (approximately) }
  80.  
  81. procedure Delay(MSecs: LongInt);
  82. var
  83.   Mark: LongInt;
  84. begin
  85.   Mark := GetTickCount + MSecs;
  86.   repeat { Wait } until GetTickCount >= Mark;
  87. end;
  88.  
  89.  
  90. {- Set Seed to CupNum * 2 and call Rand for repeatable random number
  91. sequences for positioning pebbles inside cups. This is not a very good
  92. random number generator. Don't use it for any other purpose. }
  93.  
  94. function Rand: Integer;
  95. const
  96.   A = 31415;
  97.   C = 6923;
  98. begin
  99.   Seed := (A * Seed + C) mod MaxInt;
  100.   Rand := Seed;
  101. end;
  102.  
  103.  
  104. {- Draw shadow outside bottom of cup anchored at p }
  105.  
  106. procedure Shadow(DC: HDC; P: TPoint);
  107. var
  108.   Brush: HBrush;                  { Brush for filling shadow }
  109.   OldBrush: HBrush;               { For saving DC's current brush }
  110.   CShadow: TColorRef;             { Color of shadow }
  111. begin
  112.   CShadow := RGB(0, 0, 0);
  113.   Brush := CreateSolidBrush(CShadow);
  114.   OldBrush := SelectObject(DC, Brush);
  115.   with P do
  116.     Ellipse(DC, X + 17, Y + 47, X + 52, Y + 57);
  117.   SelectObject(DC, OldBrush);
  118.   DeleteObject(Brush);
  119. end;
  120.  
  121.  
  122. {- Draw outer cup anchored at p }
  123.  
  124. procedure OuterCup(DC: HDC; P: TPoint);
  125. var
  126.   Brush: HBrush;                  { Brush for filling outer cup }
  127.   OldBrush: HBrush;               { For saving DC's current brush }
  128. begin
  129.   Brush := CreateSolidBrush(COuterCup);
  130.   OldBrush := SelectObject(DC, Brush);
  131.   with P do
  132.     Chord(DC, X, Y, X + 52, Y + 52, X, Y + 26, X + 52, Y + 26);
  133.   SelectObject(DC, OldBrush);
  134.   DeleteObject(Brush);
  135. end;
  136.  
  137.  
  138. {- Draw inner cup }
  139.  
  140. procedure InnerCup(DC: HDC; P: TPoint);
  141. var
  142.   Brush: HBrush;                  { Brush for filling inner cup }
  143.   OldBrush: HBrush;               { For saving DC's current brush }
  144.   CInnerCup: TColorRef;           { Color of inner cup }
  145. begin
  146.   CInnerCup := RGB(247, 232, 159);
  147.   Brush := CreateSolidBrush(CInnerCup);
  148.   OldBrush := SelectObject(DC, Brush);
  149.   with P do
  150.     Ellipse(DC, X, Y + 13, X + 52, Y + 39);
  151.   SelectObject(DC, OldBrush);
  152.   DeleteObject(Brush);
  153. end;
  154.  
  155.  
  156. {- Draw shadow inside bottom of cup }
  157.  
  158. procedure BottomCup(DC: HDC; P: TPoint);
  159. var
  160.   Brush: HBrush;                  { Brush for filling inner shadow }
  161.   OldBrush: HBrush;               { For saving DC's current brush }
  162. begin
  163.   Brush := CreateSolidBrush(CInnerShadow);
  164.   OldBrush := SelectObject(DC, Brush);
  165.   with P do
  166.     Ellipse(DC, X + 12, Y + 34, X + 40, Y + 40);
  167.   SelectObject(DC, OldBrush);
  168.   DeleteObject(Brush);
  169. end;
  170.  
  171.  
  172. {- Fill CupCoords array with anchor coordinates for each cup. }
  173.  
  174. procedure InitCupCoords;
  175. var
  176.   I: Integer;                     { For-loop and array index }
  177.   Xc, Yc: Integer;                { Horizontal, vertical coordinate values }
  178. begin
  179.   Yc := YBase - 28;
  180.   Xc := XBase + 60;
  181.   for I := CompLastCup downto CompFirstCup do
  182.   begin
  183.     with CupCoords[I] do
  184.     begin
  185.       X := Xc;
  186.       Y := Yc;
  187.     end;
  188.     Xc := Xc + 60;
  189.   end;
  190.   Yc := Yc + 55;
  191.   Xc := XBase + 60;
  192.   for I := HumanFirstCup to HumanLastCup do
  193.   begin
  194.     with CupCoords[I] do
  195.     begin
  196.       X := Xc;
  197.       Y := Yc;
  198.     end;
  199.     Xc := Xc + 60
  200.   end;
  201.   with CupCoords[CompKalah] do
  202.   begin
  203.     X := XBase;
  204.     Y := YBase;
  205.   end;
  206.   with CupCoords[HumanKalah] do
  207.   begin
  208.     X := XBase + 420;
  209.     Y := YBase;
  210.   end;
  211. end;
  212.  
  213.  
  214. {- Paint a rectangle filled with the specified brush }
  215.  
  216. procedure FillRect(DC: HDC; R: TRect; Brush: HBrush);
  217. var
  218.   OldBrush: HBrush;
  219. begin
  220.   OldBrush := SelectObject(DC, Brush);
  221.   with R do
  222.     Rectangle(DC, Left, Top, Right, Bottom);
  223.   SelectObject(DC, OldBrush);
  224. end;
  225.  
  226.  
  227. {- Draw an unfilled rectangle R in the current pen color }
  228.  
  229. procedure FrameRect(DC: HDC; R: TRect);
  230. begin
  231.   FillRect(DC, R, GetStockObject(null_Brush));
  232. end;
  233.  
  234.  
  235. {- Paint a rounded rectangle filled with the specified brush }
  236.  
  237. procedure FillRoundRect(DC: HDC; R: TRect; W, H: Integer; Brush: HBrush);
  238. var
  239.   OldBrush: HBrush;
  240. begin
  241.   OldBrush := SelectObject(DC, Brush);
  242.   with R do
  243.     RoundRect(DC, Left, Top, Right, Bottom, W, H);
  244.   SelectObject(DC, OldBrush);
  245. end;
  246.  
  247.  
  248. {- Draw an unfilled rounded rectangle R in the current pen color }
  249.  
  250. procedure FrameRoundRect(DC: HDC; R: TRect; W, H: Integer);
  251. begin
  252.   FillRoundRect(DC, R, W, H, GetStockObject(null_Brush));
  253. end;
  254.  
  255.  
  256. {- Erase rectangle to the specified color }
  257.  
  258. procedure EraseRect(DC: HDC; R: TRect; C: TColorRef);
  259. var
  260.   Brush, OldBrush: HBrush;
  261.   OldPen: HPen;
  262. begin
  263.   Brush := CreateSolidBrush(COuterCup);
  264.   OldBrush := SelectObject(DC, Brush);
  265.   OldPen := SelectObject(DC, GetStockObject(null_Pen));
  266.   with R do
  267.     Rectangle(DC, Left, Top, Right, Bottom);
  268.   SelectObject(DC, OldPen);
  269.   SelectObject(DC, OldBrush);
  270.   DeleteObject(Brush);
  271. end;
  272.  
  273.  
  274. {- Return new TPoint record P for a pebble to be placed at random
  275. in cup CupNum }
  276.  
  277. procedure GetPebblePoint(CupNum: CupIndex; var P: TPoint);
  278. var
  279.   Dx, Dy: Integer;
  280. begin
  281. {- Calculate random offsets within the cup }
  282.   Dx := 12 + (Abs(Rand) mod 23);  { 12 .. 34 }
  283.   Dy := 17 + (Abs(Rand) mod 12);  { 17 .. 28 }
  284. {- Assign the cup's anchor point to P }
  285.   P := CupCoords[CupNum];
  286. {- Offset the point to create the pebble position }
  287.   with P do
  288.   begin
  289.     X := X + Dx;
  290.     Y := Y + Dy;
  291.   end;
  292. end;
  293.  
  294.  
  295. {- Display how many pebbles it takes to win, placing message in upper
  296. right corner. }
  297.  
  298. procedure DisplayPebblesToWin(DC: HDC);
  299. const
  300.   maxLen = 30;                    { Maximum length of message string }
  301. var
  302.   C: array[0 .. maxLen - 4] of Char;
  303.   N: String[3];
  304.   S: String[maxLen];
  305. begin
  306.   if LoadString(HInstance, Pebbles_To_Win, C, maxLen) > 0 then
  307.   begin
  308.     Str(PebblesDiv2, N);
  309.     S := N + ' ' + StrPas(C);
  310.     TextOut(DC, XBase + 275, YBase - 85, @S[1], Length(S));
  311.   end;
  312. end;
  313.  
  314.  
  315. {- Flash outside of cup during computer moves and all captures }
  316.  
  317. procedure BlinkCup(DC: HDC; CupNum: CupIndex);
  318. var
  319.   I: Integer;
  320.   MemDC: HDC;
  321.   OldBitmap: HBitmap;
  322. begin
  323.   MemDC := CreateCompatibleDC(DC);
  324.   OldBitmap := SelectObject(MemDC, FlashBits);
  325.   for I := 1 to 8 do
  326.   begin
  327.     Delay(100);
  328.     with CupCoords[CupNum] do
  329.       BitBlt(DC, X + 2, Y + 32, X + 50, Y + 51, MemDC, 0, 0, srcInvert);
  330.   end;
  331.   SelectObject(MemDC, OldBitmap);
  332.   DeleteDC(MemDC);
  333.   Delay(16);
  334. end;
  335.  
  336.  
  337. {- Draw animation between these two coordinates. This is a modified
  338. 8-quadrant line generator. The animation is simply a blot that moves
  339. between the two points. }
  340.  
  341. procedure Animate(DC: HDC; StartX, StartY, EndX, EndY: Integer);
  342.  
  343. const
  344.  
  345.    speed = 24;     { Number of pixels between animation frames.
  346.                      Higher values = faster speeds. Might make
  347.                      this variable someday to account for
  348.                      different processor speeds. }
  349. var
  350.  
  351.    Dx, Dy: Integer;               { Delta (i.e. change in) values }
  352.    PebPt: TPoint;                 { Pebble point location }
  353.    Count: Integer;                { Controls when plotting occurs }
  354.    OldMode: Integer;              { Saves current DC's display mode }
  355.  
  356.  
  357.    procedure SetPt(var P: TPoint; X, Y: Integer);
  358.    begin
  359.      P.X := X;
  360.      P.Y := Y;
  361.    end;
  362.  
  363.  
  364. {- Draw animation frame at this location after erasing frame at
  365. previous location. Actual plotting only occurs in multiples of
  366. the speed value. }
  367.  
  368.    procedure Plot(X, Y: Integer);
  369.    begin
  370.      if (Count mod Speed) = 0 then
  371.      begin
  372.        PutPebble(DC, PebPt);      { Erase old pebble }
  373.        SetPt(PebPt, X, Y);        { Move to new location }
  374.        PutPebble(DC, PebPt);      { Draw pebble }
  375.      end;
  376.      Inc(count);
  377.    end;
  378.  
  379.    procedure Octant1;
  380.    var
  381.      CntDwn, Err: Integer;
  382.    begin
  383.      CntDwn := Dx + 1;
  384.      Err := -Dx div 2;
  385.      repeat
  386.        repeat
  387.          Plot(StartX, StartY);
  388.          CntDwn := CntDwn - 1;
  389.          if CntDwn < 0 then Exit;
  390.          StartX := StartX + 1;
  391.          Err := Err - Dy
  392.        until Err >= 0;
  393.        StartY := StartY - 1;
  394.        Err := Err - Dx;
  395.      until false;
  396.    end;
  397.  
  398.    procedure Octant2;
  399.    var
  400.      CntDwn, Err: Integer;
  401.    begin
  402.      CntDwn := -Dy + 1;
  403.      Err := Dy div 2;
  404.      repeat
  405.        repeat
  406.          Plot(StartX, StartY);
  407.          CntDwn := CntDwn - 1;
  408.          if CntDwn < 0 then Exit;
  409.          StartY := StartY - 1;
  410.          Err := Err + Dx;
  411.        until Err >= 0;
  412.        StartX := StartX + 1;
  413.        Err := Err + Dy;
  414.      until false;
  415.    end;
  416.  
  417.    procedure Octant3;
  418.    var
  419.      CntDwn, Err: Integer;
  420.    begin
  421.      CntDwn := -Dy + 1;
  422.      Err := Dy div 2;
  423.      repeat
  424.        repeat
  425.          Plot(StartX, StartY);
  426.          CntDwn := CntDwn - 1;
  427.          if CntDwn < 0 then Exit;
  428.          StartY := StartY - 1;
  429.          Err := Err - Dx;
  430.        until Err >= 0;
  431.        StartX := StartX - 1;
  432.        Err := Err + Dy;
  433.      until false;
  434.    end;
  435.  
  436.    procedure Octant4;
  437.    var
  438.      CntDwn, Err: Integer;
  439.    begin
  440.      CntDwn := -Dx + 1;
  441.      Err := Dx div 2;
  442.      repeat
  443.        repeat
  444.          Plot(StartX, StartY);
  445.          CntDwn := CntDwn - 1;
  446.          if CntDwn < 0 then Exit;
  447.          StartX := StartX - 1;
  448.          Err := Err - Dy;
  449.        until Err >= 0;
  450.        StartY := StartY - 1;
  451.        Err := Err + Dx;
  452.      until false;
  453.    end;
  454.  
  455.    procedure Octant5;
  456.    var
  457.      CntDwn, Err: Integer;
  458.    begin
  459.      CntDwn := -Dx + 1;
  460.      Err := Dx div 2;
  461.      repeat
  462.        repeat
  463.          Plot(StartX, StartY);
  464.          CntDwn := CntDwn - 1;
  465.          if CntDwn < 0 then Exit;
  466.          StartX := StartX - 1;
  467.          Err := Err + Dy;
  468.        until Err >= 0;
  469.        StartY := StartY + 1;
  470.        Err := Err + Dx;
  471.      until false;
  472.    end;
  473.  
  474.    procedure Octant6;
  475.    var
  476.      CntDwn, Err: Integer;
  477.    begin
  478.      CntDwn := Dy + 1;
  479.      Err := -Dy div 2;
  480.      repeat
  481.        repeat
  482.          Plot(StartX, StartY);
  483.          CntDwn := CntDwn - 1;
  484.          if CntDwn < 0 then Exit;
  485.          StartY := StartY + 1;
  486.          Err := Err - Dx;
  487.        until Err >= 0;
  488.        StartX := StartX - 1;
  489.        Err := Err - Dy;
  490.      until false;
  491.    end;
  492.  
  493.    procedure Octant7;
  494.    var
  495.      CntDwn, Err: Integer;
  496.    begin
  497.      CntDwn := Dy + 1;
  498.      Err := -Dy div 2;
  499.      repeat
  500.        repeat
  501.          Plot(StartX, StartY);
  502.          CntDwn := CntDwn - 1;
  503.          if CntDwn < 0 then Exit;
  504.          StartY := StartY + 1;
  505.          Err := Err + Dx;
  506.        until Err >= 0;
  507.        StartX := StartX + 1;
  508.        Err := Err - Dy;
  509.      until false;
  510.    end;
  511.  
  512.    procedure Octant8;
  513.    var
  514.      CntDwn, Err: Integer;
  515.    begin
  516.      CntDwn := Dx + 1;
  517.      Err := -Dx div 2;
  518.      repeat
  519.        repeat
  520.          Plot(StartX, StartY);
  521.          CntDwn := CntDwn - 1;
  522.          if CntDwn < 0 then Exit;
  523.          StartX := StartX + 1;
  524.          Err := Err + Dy;
  525.        until Err >= 0;
  526.        StartY := StartY + 1;
  527.        Err := Err - Dx;
  528.      until false;
  529.    end;
  530.  
  531. begin
  532.  
  533.    Delay(200);
  534.  
  535.    Count := 1;          { Controls when plotting occurs }
  536.  
  537.    OldMode := SetROP2(DC, r2_NotXorPen);    { Use XOR drawing mode }
  538.  
  539.  
  540. {- Initialize first frame so it can be erased by Plot. This
  541.    is the position of the pebble in its group. }
  542.  
  543.    SetPt(PebPt, StartX, StartY);
  544.  
  545.    Dx := EndX - StartX;
  546.    Dy := EndY - StartY;
  547.  
  548.    if Dx > 0 then
  549.    begin      { Right half }
  550.      if Dy < 0 then
  551.      begin    { Top quadrant }
  552.        if -Dy > Dx then Octant2 else Octant1;
  553.      end else
  554.      begin    { Bottom quadrant }
  555.        if Dy > Dx then Octant7 else Octant8;
  556.      end;
  557.    end else
  558.    begin      { Left half }
  559.      if Dy < 0 then
  560.      begin    { Top quadrant }
  561.        if Dy > Dx then Octant4 else Octant3;
  562.      end else
  563.      begin    { Bottom quadrant }
  564.        if Dy > -Dx then Octant6 else Octant5;
  565.      end;
  566.    end;
  567.  
  568.    PutPebble(DC, PebPt);     { Remove final plot }
  569.  
  570.    SetROP2(DC, OldMode);     { Restore DC's drawing mode }
  571.  
  572. end;
  573.  
  574.  
  575. {- Display message in global MessageRect "window" reading the string
  576. with resource id = MessageID, indexed by StrIndex parameter. If
  577. StrIndex = 0, the message window is cleared. Save StrIndex in global
  578. CurrentMessage variable, ensuring that the correct message will always
  579. be redisplayed during update (paint) events. }
  580.  
  581. procedure DisplayMessage(DC: HDC; StrIndex: Integer);
  582. const
  583.   maxlen = 80;
  584. var
  585.   Message: array[0 .. maxLen] of Char;      { Holds string resource }
  586.   MessageLen: Integer;                      { Length of message }
  587.   Extent: LongInt;                          { Message height and width in pixels }
  588. begin
  589.   CurrentMessage := StrIndex;
  590.   if LoadString(HInstance, CurrentMessage, Message, maxLen) > 0 then
  591.   with MessageRect do
  592.   begin
  593.     MessageLen := StrLen(Message);
  594.     Extent := GetTextExtent(DC, Message, MessageLen);
  595.     FillRect(DC, MessageRect, GetStockObject(white_Brush));
  596.     TextOut(DC,
  597.       Left + (((Right - Left) - LOWORD(Extent)) div 2),
  598.       Top + (((Bottom - Top) - HIWORD(Extent)) div 2),
  599.       Message, StrLen(Message));
  600.   end;
  601. end;
  602.  
  603.  
  604. {- Draw one pebble at P.X, P.Y }
  605.  
  606. procedure PutPebble(DC: HDC; P: TPoint);
  607. var
  608.   OldBrush: HBrush;
  609. begin
  610.   OldBrush := SelectObject(DC, GetStockObject(gray_Brush));
  611.   with P do
  612.     Ellipse(DC, X, Y, X + 7, Y + 7);
  613.   SelectObject(DC, OldBrush);
  614. end;
  615.  
  616.  
  617. {- Move a pebble from Fp (from point) to Tp (to point) }
  618.  
  619. procedure MovePebble(DC: HDC; Fp, Tp: TPoint);
  620. begin
  621.   Animate(DC, Fp.X, Fp.Y, Tp.X, Tp.Y);
  622.   PutPebble(DC, Tp);
  623. end;
  624.  
  625.  
  626. {- Erase inside of cup, removing any pebbles inside }
  627.  
  628. procedure EmptyTheCup(DC: HDC; CupNum: CupIndex);
  629. begin
  630.   InnerCup(DC, CupCoords[CupNum]);
  631.   BottomCup(DC, CupCoords[CupNum]);
  632. end;
  633.  
  634.  
  635. {- Display N on face of this cup, representing the number
  636. of pebbles in the cup. }
  637.  
  638. procedure DisplayNumber(DC: HDC; CupNum: CupIndex; N: Integer);
  639. var
  640.   S: String[3];
  641.   R: TRect;
  642.   OldBkMode: Integer;
  643.   OldTextColor: TColorRef;
  644. begin
  645.   Str(N, S);
  646.   with CupCoords[CupNum] do
  647.   begin
  648.     SetRect(R, X + 18, Y + 40, X + 34, Y + 52);
  649.     EraseRect(DC, R, COuterCup);
  650.     OldBkMode := SetBkMode(DC, Transparent);
  651.     OldTextColor := SetTextColor(DC, RGB(255, 255, 255));
  652.     R.Top := R.Top - 2;
  653.     R.Bottom := R.Bottom + 4;
  654.     DrawText(DC, @S[1], Length(S), R, dt_Center);
  655.     SetTextColor(DC, OldTextColor);
  656.     SetBkMode(DC, OldBkMode);
  657.   end;
  658. end;
  659.  
  660.  
  661. {- Draw NumPebbles pebbles inside cup cupnum.  Erases cup before
  662. drawing to make certain it's empty. Important to pass correct number
  663. of pebbles in cup to this procedure, which does not refer to global
  664. Gameboard. This makes it easier to write the graphics MakeMove routine,
  665. which can display a number on the cup temporarily even if that does not
  666. match the number of pebbles actually in the cup. }
  667.  
  668. procedure DrawPebbles(DC: HDC; CupNum: CupIndex; NumPebbles: Integer);
  669. var
  670.   PebblePoint: TPoint;
  671.   I: Integer;
  672. begin
  673.   EmptyTheCup(DC, CupNum);        { Empty cup of any pebbles }
  674.   Seed := CupNum;                 { Start random sequence for this cup }
  675.   for I := 1 to NumPebbles do
  676.   begin
  677.     GetPebblePoint(CupNum, PebblePoint);
  678.     PutPebble(DC, PebblePoint);
  679.   end;
  680.   DisplayNumber(DC, CupNum, NumPebbles);
  681. end;
  682.  
  683.  
  684. {- Move any pebbles out of cup, displaying them above board for
  685. computer's side or below for human's cups. Erases face of cup, removing
  686. any number there. Call this procedure for mouse clicks inside cup or for
  687. computer's move as the first part of the animation sequence. Set Side to
  688. human or computer and be sure that CupNum is appropriate for this side. }
  689.  
  690. procedure PickUpPebbles(DC: HDC; CupNum: CupIndex; Side: Integer;
  691.   var Gameboard: Board);
  692. var
  693.   PebblePoint: TPoint;
  694.   I, NumPebbles, Offset: Integer;
  695.   OldMode: Integer;
  696. begin
  697.   NumPebbles := Gameboard[CupNum];
  698.   if NumPebbles = 0 then Exit;
  699.   if Side = human then
  700.     Offset := 75
  701.   else
  702.     Offset := -50;
  703.   EmptyTheCup(DC,CupNum);
  704.   OldMode := SetROP2(DC, r2_NotXorPen);
  705.   Seed := CupNum;
  706.   for I := 1 to NumPebbles do
  707.   begin
  708.     GetPebblePoint(CupNum, PebblePoint);
  709.     with PebblePoint do
  710.       Y := Y + Offset;
  711.     PutPebble(DC, PebblePoint);
  712.   end;
  713.   SetROP2(DC, OldMode);
  714. end;
  715.  
  716.  
  717. {- Draw gameboard and message window using global XBase, YBase. }
  718.  
  719. procedure DrawGameboard(DC: HDC; var Gameboard: Board);
  720. var
  721.   I, J, H, V: Integer;
  722.   R1, R2: TRect;
  723.   P: TPoint;
  724.   Pen: HPen;            { Pen handle for outlines }
  725.   OldPen: HPen;         { For saving DC's current pen }
  726.   S: String[1];         { For cup labels }
  727. begin
  728.  
  729.   Pen := CreatePen(ps_Solid, 1, CPen);
  730.   OldPen := SelectObject(DC, Pen);
  731.  
  732.  
  733. {- Initialize R1 to main board location }
  734.  
  735.   SetRect(R1, XBase + 2, YBase, XBase + 464, YBase + 100);
  736.  
  737.  
  738. {- Draw the message center "window," which is not a real window, just a
  739. box in which messages appear. The window appears with a shadow
  740. 10 pixels below and to the right. The global messageRect variable is
  741. used by the DisplayMessage procedure. }
  742.  
  743.   MessageRect := R1;
  744.   with R1 do
  745.   begin
  746.     OffsetRect(MessageRect, 0, (Bottom - Top) + 20);
  747.     InflateRect(MessageRect, -((Right - Left) div 4), -((Bottom - Top) div 4));
  748.   end;
  749.   R2 := MessageRect;
  750.   OffsetRect(R2, 10, 10);
  751.   FillRect(DC, R2, GetStockObject(dkGray_Brush));
  752.   FrameRect(DC, MessageRect);
  753.   InflateRect(MessageRect, -1, -1);
  754.   FillRect(DC, MessageRect, GetStockObject(white_Brush));
  755.  
  756.  
  757. {- Draw the boards on which the cups rest }
  758.  
  759.   R2 := R1;
  760.   OffsetRect(R2, 10, 10);
  761.   FrameRoundRect(DC, R2, 20, 20);
  762.   InflateRect(R2, -1, -1);
  763.   FillRoundRect(DC, R2, 20, 20, GetStockObject(dkGray_Brush));
  764.   FrameRoundRect(DC, R1, 20, 20);
  765.   InflateRect(R1, -1, -1);
  766.   FillRoundRect(DC, R1, 20, 20, GetStockObject(ltGray_Brush));
  767.  
  768.  
  769. {- Draw the cups }
  770.  
  771.   for I := 0 to Maxcupindex do
  772.   begin
  773.     P := Cupcoords[I];
  774.     Shadow(DC, P);
  775.     OuterCup(DC, P);
  776.     DrawPebbles(DC, I, Gameboard[I]);
  777.   end;
  778.  
  779.  
  780. {- Label cups with their numbers }
  781.  
  782.   for I := HumanFirstCup to HumanLastCup do
  783.   begin
  784.     Str(I, S);
  785.     with Cupcoords[I] do
  786.       TextOut(DC, X + 22, Y + 90, @S[1], 1);
  787.   end;
  788.  
  789.  
  790. {- Display the global CurrentMessage in the window. This takes care
  791. of redisplaying messages during repaint events (which also call
  792. DrawGameboard). }
  793.  
  794.   DisplayMessage(DC, CurrentMessage);
  795.  
  796.  
  797. {- Display how many pebbles it takes to win }
  798.  
  799.   DisplayPebblesToWin(DC);
  800.  
  801.  
  802. {- Clean up }
  803.  
  804.   SelectObject(DC, OldPen);
  805.   DeleteObject(Pen);
  806.  
  807. end;
  808.  
  809.  
  810. {- Make visual move. Program also must call MakeMove to make the same
  811. move internally. MakeGraphMove is purely visual. }
  812.  
  813. procedure MakeGraphMove(DC: HDC; Position: BoardRec; Side: Integer;
  814.   Move: OneMove);
  815. var
  816.   Cup: CupIndex;
  817.   Pebbles: Integer;
  818.   PlayerKalah: CupIndex;
  819.   OpponentKalah: CupIndex;
  820.   CaptureCup: CupIndex;
  821.   FirstCup: CupIndex;
  822.   LastCup: CupIndex;
  823.   CupWasEmpty: Boolean;
  824.   CapturedPebbles: Integer;
  825.   OtherSide: Integer;
  826.  
  827. {- The pebbles are above or below the cup being moved. Take one pebble
  828. and move it to cup number ToCup. NumPebbles equals the total number of
  829. pebbles above or below FromCup. Sie indicates who FromCup belongs to.
  830. By the time ZoomPebble is called, the move has been made on Position.Gameboard. }
  831.  
  832.   procedure ZoomPebble(FromCup, ToCup, NumPebbles, Side: Integer);
  833.   var
  834.     Fp, Tp: TPoint;               { From point, to point }
  835.     Offset: Integer;              { Vertical offset to pebbles }
  836.     I: Integer;
  837.   begin
  838.     if Side = human then
  839.       Offset := 75                { Pebbles are below cup }
  840.     else
  841.       Offset := -50;              { Pebbles are above cup }
  842.     Seed := FromCup;
  843.     for I := 1 to NumPebbles do
  844.       GetPebblePoint(FromCup, Fp);
  845.     Fp.Y := Fp.Y + Offset;
  846.     Seed := ToCup;
  847.     for I := 1 to Position.Gameboard[ToCup] do
  848.       GetPebblePoint(ToCup, Tp);
  849.     MovePebble(DC, Fp, Tp);
  850.   end;
  851.  
  852.  
  853. {- Initialize for graphics move }
  854.  
  855.   procedure InitGmove;
  856.   begin
  857.     if Side = computer then
  858.       BlinkCup(DC, Move);
  859.     PickUpPebbles(DC, Move, Side, Position.Gameboard);
  860.     DisplayNumber(DC, Move, 0);
  861.     if Side = computer then
  862.     begin { Set up for computer }
  863.       OtherSide := human;
  864.       PlayerKalah := CompKalah;
  865.       OpponentKalah := HumanKalah;
  866.       FirstCup := CompFirstCup;
  867.       LastCup := CompLastCup;
  868.     end else
  869.     begin { Set up for human }
  870.       OtherSide := computer;
  871.       PlayerKalah := HumanKalah;
  872.       OpponentKalah := CompKalah;
  873.       FirstCup := HumanFirstCup;
  874.       LastCup := HumanLastCup;
  875.     end;
  876.   end;
  877.  
  878.  
  879. {- Make the move, distributing pebbles counter-clockwise }
  880.  
  881.   procedure MakeGMove;
  882.   begin
  883.     with Position do
  884.     begin
  885.       Cup := Move;
  886.       Pebbles := Gameboard[Move];
  887.       Gameboard[Move] := 0;
  888.       while Pebbles > 0 do
  889.       begin
  890.         if Cup = MaxCupIndex then
  891.           Cup := 0
  892.         else
  893.           Cup := Cup + 1;
  894.         if Cup <> OpponentKalah then  { Skip opponent's kalah }
  895.         begin
  896.           CupWasEmpty := Gameboard[Cup] = 0;
  897.           Gameboard[Cup] := Gameboard[Cup] + 1;
  898.           ZoomPebble(Move, Cup, Pebbles, Side);
  899.           Pebbles := Pebbles - 1;
  900.           DisplayNumber(DC, Cup, Gameboard[Cup])
  901.         end;
  902.       end;
  903.       GoAgain := (Cup = PlayerKalah)
  904.     end;
  905.   end;
  906.  
  907.  
  908. {- Check for captures when player's last stone drops into one of the
  909. player's own empty cups and an opposite cup contains pebbbles. }
  910.  
  911.   procedure DoCapture;
  912.   begin
  913.     with Position do
  914.     begin
  915.       if not GoAgain then
  916.       if CupWasEmpty then
  917.       if FirstCup <= Cup then
  918.       if Cup <= LastCup then
  919.       begin  { Capture }
  920.         CaptureCup := maxCups - Cup;
  921.         CapturedPebbles := Gameboard[CaptureCup];
  922.         if CapturedPebbles > 0 then
  923.         begin
  924.           DisplayMessage(DC, Capture);
  925.           BlinkCup(DC, CaptureCup);
  926.           PickUpPebbles(DC, CaptureCup, OtherSide, Gameboard);
  927.           DisplayNumber(DC, CaptureCup, 0);
  928.           Gameboard[CaptureCup] := 0;
  929.           while CapturedPebbles > 0 do
  930.           begin
  931.             Gameboard[PlayerKalah] :=
  932.               Gameboard[PlayerKalah] + 1;
  933.             ZoomPebble(CaptureCup, PlayerKalah,
  934.               CapturedPebbles, OtherSide);
  935.             DisplayNumber(DC, PlayerKalah,
  936.               Gameboard[PlayerKalah]);
  937.             CapturedPebbles := CapturedPebbles - 1;
  938.           end;
  939.           BlinkCup(DC, Cup);
  940.           PickUpPebbles(DC, Cup, Side, Gameboard);
  941.           DisplayNumber(DC, Cup, 0);
  942.           Gameboard[Cup] := 0;
  943.           Gameboard[PlayerKalah] :=
  944.             Gameboard[PlayerKalah] + 1;
  945.           ZoomPebble(Cup, PlayerKalah, 1, Side);
  946.           DisplayNumber(DC, PlayerKalah,
  947.             Gameboard[PlayerKalah]);
  948.         end;
  949.       end;
  950.     end;
  951.   end;
  952.  
  953.  
  954. {- Move all stones from FirstCup to LastCup into this kalah. This action
  955. occurs only when the opposite side's cups are all empty. }
  956.  
  957.   procedure moveallpebbles(var Gameboard: Board; TheSide: Integer;
  958.     FirstCup, LastCup, Kalah: CupIndex);
  959.   var
  960.     I, Pebbles: Integer;
  961.   begin
  962.     with Position do
  963.     begin
  964.       Pebbles := 0;
  965.       for I := FirstCup to LastCup do
  966.       if Gameboard[I] > 0 then
  967.       begin
  968.         BlinkCup(DC, I);
  969.         PickUpPebbles(DC, I, TheSide, Gameboard);
  970.         DisplayNumber(DC, I, 0);
  971.         Pebbles := Gameboard[I];
  972.         Gameboard[I] := 0;
  973.         while Pebbles > 0 do
  974.         begin
  975.           Gameboard[Kalah] := Gameboard[Kalah] + 1;
  976.           ZoomPebble(I, Kalah, Pebbles, TheSide);
  977.           DisplayNumber(DC, Kalah, Gameboard[Kalah]);
  978.           Pebbles := Pebbles - 1
  979.         end;
  980.       end;
  981.     end;
  982.   end;
  983.  
  984.  
  985. begin
  986.   InitGMove;     { Initialize various things }
  987.   MakeGMove;     { Make first part of move }
  988.   DoCapture;     { Check for and make any captures }
  989.  
  990. {- Check for special condition where either side's cups are
  991. all empty. In this case, unless the side that has gone out
  992. has won, the opposite side moves all pebbles into that side's
  993. kalah and wins. }
  994.  
  995.   with Position do
  996.   begin
  997.     if CupsEmpty(Gameboard, CompFirstCup, CompLastCup) then
  998.     begin
  999.       DisplayMessage(DC, Im_out);
  1000.       if Gameboard[CompKalah] < PebblesDiv2 then
  1001.         MoveAllPebbles(Gameboard, human, HumanFirstCup,
  1002.           HumanLastCup, HumanKalah)
  1003.     end else
  1004.     if CupsEmpty(Gameboard, HumanFirstCup, HumanLastCup) then
  1005.     begin
  1006.       DisplayMessage(DC, Youre_out);
  1007.       if Gameboard[HumanKalah] < PebblesDiv2 then
  1008.         MoveAllPebbles(Gameboard, computer, CompFirstCup,
  1009.           CompLastCup, CompKalah)
  1010.     end;
  1011.   end;
  1012. end;
  1013.  
  1014.  
  1015. {- Initialize UGraphics unit }
  1016.  
  1017. procedure InitUGraphics;
  1018. begin
  1019.   InitCupCoords;                  { Initialize cup coordinates }
  1020.   FlashBits := CreateBitmap(48, 19, 1, 1, @FlashBuf);
  1021. end;
  1022.  
  1023.  
  1024. end.
  1025.  
  1026.  
  1027. { ----------------------------------------------------------------
  1028.   Copyright (c) 1991 by Swan Software. All rights reserved.
  1029.   Revision 1.00    Date: 8/21/1991
  1030.   ---------------------------------------------------------------- }
  1031.